package simtester.cloud

import static org.junit.Assert.*

import java.security.PublicKey
import java.util.concurrent.TimeUnit
import net.schmizz.sshj.SSHClient
import net.schmizz.sshj.common.Buffer
import net.schmizz.sshj.common.IOUtils
import net.schmizz.sshj.connection.channel.direct.Session;
import net.schmizz.sshj.connection.channel.direct.Session.Command;
import net.schmizz.sshj.connection.channel.direct.Session.Shell;
import net.schmizz.sshj.transport.verification.HostKeyVerifier

import org.junit.After
import org.junit.Before
import org.junit.BeforeClass
import org.junit.Test

class CloudRemoteTests {
    static testCredentials
    static testEndpoint
    static testAuthkey
    static testScript
    def testInstanceIds
    
    @BeforeClass
    static void setUpClass() {
        def config = new ConfigSlurper().parse(new File('conf/SimTesterConfig.groovy').toURI().toURL())
        testCredentials = config.aws.credentialsfile
        testEndpoint    = config.aws.endpoint
        testAuthkey     = config.aws.key
        
        testScript = '''\
package simtester.agent
import geb.*
import java.util.logging.*
class AgentScript {
  static runtest() {
    def log = Logger.getLogger("simtester.agent")
    Browser.drive {
      go 'http://www.google.com'
      log.info("Title: $title")
      assert title == 'Google'
    }
  }
}'''
    }
    
    @Before
    void setUp() {
        testInstanceIds = []
    }
    
    @After
    void tearDown() {
        def cloud = new Cloud(creds: testCredentials, endpoint: testEndpoint, key: testAuthkey)
        cloud.removeInstances(testInstanceIds)
    }
    
    @Test
    void createBlankTestAgent() {
        def cloud = new Cloud(creds: testCredentials, endpoint: testEndpoint, key: testAuthkey)

        def agentjar = new File('resources/agent.jar')
        def url = 'http://localhost:8080/server/'
        def agent = cloud.createBlankTestAgent(agentpkg: agentjar)
        
        assert agent.id
        testInstanceIds << agent.id
        
        def expectedFiles = [
            "/home/ec2-user/simtester/agent/agent.jar"
        ]
        
        expectedFiles.each { filename ->
            assert agent.fileExists(filename)
        }
        
        def unexpectedFiles = [
            "/home/ec2-user/simtester/agent/AgentConfig.groovy",
            "/home/ec2-user/simtester/agent/AgentScript.groovy"
        ]
        
        unexpectedFiles.each { filename ->
            assert ! agent.fileExists(filename)
        }
    }
    
    @Test
    void createTestAgent() {
        def cloud = new Cloud(creds: testCredentials, endpoint: testEndpoint, key: testAuthkey)

        def agentjar = new File('resources/agent.jar')
        def url = 'http://localhost:8080/server/'
        def runId = '111111111111111111111111'
        def numRepeats = 2
        def agent = cloud.createTestAgent(
            script:    testScript,
            agentpkg:  agentjar,
            url:       url,
            testrunId: runId,
            repeats:   numRepeats)
        
        assert agent.id
        testInstanceIds << agent.id
        
        def expectedFiles = [
            "/home/ec2-user/simtester/agent/agent.jar",
            "/home/ec2-user/simtester/agent/AgentConfig.groovy",
            "/home/ec2-user/simtester/agent/AgentScript.groovy"
        ]
        
        expectedFiles.each { filename ->
            agent.fileExists(filename)
        }
        
        assert agent.viewText("/home/ec2-user/simtester/agent/AgentScript.groovy") == testScript
        
        def configText = agent.viewText("/home/ec2-user/simtester/agent/AgentConfig.groovy")
        assert configText.contains("serverurl = '$url'")
        assert configText.contains("testrunid = '$runId'")
        assert configText.contains("repeats = $numRepeats")
        
        try {
            Utils.waitFor(limit: 180000, step: 2000) {
                agent.fileExists('/home/ec2-user/simtester/agent/agent.log')
            }
        } catch (TimeoutException e) {
            fail 'Timeout waiting for existence of agent.log'
        }
    }
    
    @Test
    void setupTestAgent() {
        def cloud = new Cloud(creds: testCredentials, endpoint: testEndpoint, key: testAuthkey)
        
        def agentjar = new File('resources/agent.jar')
        def url = 'http://localhost:8080/server/'
        def instance = cloud.createBlankTestAgent(
            agentpkg: agentjar,
            url:      url)

        instance.exec("echo some text goes here > /home/ec2-user/simtester/agent/AgentConfig.groovy")
        instance.exec("echo some text goes here > /home/ec2-user/simtester/agent/AgentScript.groovy")
        
        def runId = '111111111111111111111111'
        def numRepeats = 2
        def firstuser = 2
        def numUsers = 7
        def agent = cloud.setupTestAgent(
            instanceId: instance.id,
            script:     testScript,
            url:        url,
            testrunId:  runId,
            repeats:    numRepeats,
            firstuser:  firstuser,
            users:      numUsers)
        
        assert agent.id
        testInstanceIds << agent.id
        
        def expectedFiles = [
            "/home/ec2-user/simtester/agent/agent.jar",
            "/home/ec2-user/simtester/agent/AgentConfig.groovy",
            "/home/ec2-user/simtester/agent/AgentScript.groovy"
        ]
        
        expectedFiles.each { filename ->
            agent.fileExists(filename)
        }
        
        assert agent.viewText("/home/ec2-user/simtester/agent/AgentScript.groovy") == testScript
        
        def configText = agent.viewText("/home/ec2-user/simtester/agent/AgentConfig.groovy")
        assert configText.contains("serverurl = '$url'")
        assert configText.contains("testrunid = '$runId'")
        assert configText.contains("repeats = $numRepeats")
        assert configText.contains("firstuser = $firstuser")
        assert configText.contains("users = $numUsers")
        
        try {
            Utils.waitFor(limit: 180000, step: 2000) {
                agent.fileExists('/home/ec2-user/simtester/agent/agent.log')
            }
        } catch (TimeoutException e) {
            fail 'Timeout waiting for existence of agent.log'
        }
    }
    
    @Test
    void setupTestAgentWithDuration() {
        def cloud = new Cloud(creds: testCredentials, endpoint: testEndpoint, key: testAuthkey)

        def agentjar = new File('resources/agent.jar')
        def url = 'http://localhost:8080/server/'
        def instance = cloud.createBlankTestAgent(
            agentpkg: agentjar,
            url:      url)
        
        def runId = '111111111111111111111111'
        def duration = 225234
        def agent = cloud.setupTestAgent(
            instanceId: instance.id,
            script:     testScript,
            url:        url,
            testrunId:  runId,
            duration:   duration)
        
        assert agent.id
        testInstanceIds << agent.id

        def configText = agent.viewText("/home/ec2-user/simtester/agent/AgentConfig.groovy")
        assert configText.contains("duration = $duration")
        
        try {
            Utils.waitFor(limit: 180000, step: 2000) {
                agent.fileExists('/home/ec2-user/simtester/agent/agent.log')
            }
        } catch (TimeoutException e) {
            fail 'Timeout waiting for existence of agent.log'
        }
    }
    
    @Test
    void createInstance() {
        def cloud = new Cloud(creds: testCredentials, endpoint: testEndpoint, key: testAuthkey)
        def instance = cloud.createInstance(type: 't1.micro', imageId: 'ami-1bd68a5e')
        assert instance.id
        testInstanceIds << instance.id
        
        def elapsed = Utils.waitFor(limit: 120000, step: 2000) {
            instance.state == 'running'
        }
        
        assert instance.id =~ /[\w\-]+/
        assert instance.publicDnsName =~ /[\w\.]+/
        assert instance.publicIpAddress =~ /[\d\.]+/
        assert instance.state =~ /\w+/
        assert instance.amiId =~ /[\w\-]+/
        assert instance.type =~ /[\w\.]+/
        
        def found = cloud.findInstance(instance.id)
        assert found.id == instance.id
    }
    
    @Test
    void findInstanceThatDoesNotExist() {
        def cloud = new Cloud(creds: testCredentials, endpoint: testEndpoint, key: testAuthkey)
        def found = cloud.findInstance('i-aaaaaaaa')
        assert found == null
    }
    
    @Test
    void findInstanceException() {
        def cloud = new Cloud(creds: testCredentials, endpoint: testEndpoint, key: testAuthkey)
        
        try {
            def found = cloud.findInstance('bogusid')
            fail 'failed to throw exception'
        } catch (CloudException e) {
            assert e.message =~ /Invalid id/
        }
        
    }

}

//TODO: Test for image ID doesnt match architecture?
    /*Feb 7, 2012 10:40:02 PM com.amazonaws.http.AmazonHttpClient executeHelper
    INFO: Sending Request: POST https://ec2.us-west-1.amazonaws.com / Parameters: (ImageId: ami-1bd68a5e, SecurityGroup.1: cmpe295-security-group, Action: RunInstances, MinCount: 1, Version: 2011-05-15, KeyName: cmpe295-keypair, MaxCount: 1, ) 
    Feb 7, 2012 10:40:04 PM com.amazonaws.http.AmazonHttpClient handleErrorResponse
    INFO: Received error response: Status Code: 400, AWS Request ID: a923ae78-5b78-4bc5-bad4-d20b8708a73a, AWS Error Code: InvalidParameterValue, AWS Error Message: The requested instance type's architecture (i386) does not match the architecture in the manifest for ami-1bd68a5e (x86_64)
    Caught Exception: The requested instance type's architecture (i386) does not match the architecture in the manifest for ami-1bd68a5e (x86_64)
    Reponse Status Code: 400
    Error Code: InvalidParameterValue
    Request ID: a923ae78-5b78-4bc5-bad4-d20b8708a73a*/